<?php
/**
 * Framework_DB_Datagrid 
 * @author      Ian Szewczyk <roamzero@gmail.com>
 * @copyright   (c) 2007 Ian Szewczyk. All rights reserved.
 * @license     http://www.opensource.org/licenses/bsd-license.php
 * @package Framework
 * @subpackage DB
 * @filesource
 */


/**
 * Framework_DB_Datagrid  
 *
 * Class for generating paged result sets from teh database
 *
 * @author 		Ian Szewczyk <roamzero@gmail.com>
 * @package 	Framework
 * @subpackage 	DB
 */
class Framework_DB_Datagrid extends Framework_Object_DB {
	
	/**
     * $data
     *
     * @access      public
     * @var         object		$data      Result object
     */
	public $data;
	
	/**
     * $currentPage
     *
     * @access      public
     * @var         integer		$curentPage       The current page
     */
	public $currentPage = 1;
	
	/**
     * $totalRecords
     *
     * @access      public
     * @var         integer		$totalRecords      The number of records found
     */
	public $totalRecords;
	
	/**
     * $pageSize
     *
     * @access      public
     * @var         integer		$pageSize      The number of records per page
     */
	public $pageSize;
	
	/**
     * $sortColumn
     *
     * @access      private
     * @var         string		$sortColumn      The column to sort by
     */
	private $sortColumn;
	
	/**
     * $direction
     *
     * @access      private
     * @var         string		$direction      Direction to sort by (ASC/DESC)
     */
	private $direction;
	
	/**
     * $prefix
     *
     * @access      private
     * @var         string		$prefix      Prefix for the GET variable (defaults to 'page')
     */
	private $prefix;
	
	/**
     * $tableInfo
     *
     * @access      private
     * @var         array		$tableInfo     Array of data about the resulting query
     */
	private $tableInfo;
	
	/**
     * $pagerFile
     *
     * @access      private
     * @var         string		$pagerFile     Used to construct the GET query
     */
	private $pagerFile;
	
	/**
     * $pagerFile
     *
     * @static 
     * @access      private
     * @var         array		$urlQueries    Collection of all GET queries in the page
     */
	private static $urlQueries = array();
	
	/**
	 * __construct
	 *
	 * @access 		public
	 * @param 		object 			$dbObject 		Valid DB object to query
	 * @param 		array 			$queryArray 	Query Array
	 * @param 		integer 		$pageSize 		Number of items per page
	 * @param 		string 			$prefix 		GET query prefix
	 * @param 		mixed 			$cacheHandler 	Cache handler to pass to the queries
	 * @return 		void
	 */
	public function __construct($dbObject, $queryArray, $pageSize = 10, $prefix = 'page', $cacheHandler = null){
		
		parent::__construct();
		
		$this->prefix = $prefix;
		
		// Attempt to parse GET argument(s)
		$GET = Framework::$request->get();
		foreach ($GET as $key => $item) {
			
			// Existing match
			if(preg_match('/^'.$prefix.'\/\d+(\/(ASC|DESC)+)*(\/[a-zA-Z]+)*/',$key)) {
				self::$urlQueries[$prefix] = $key;
				$values = split('/',$key);
				$this->currentPage = (int)$values[1];
				
				// sort column
				if($values[2] && $dbObject->getCol($values[2])) {
					$this->sortColumn = $values[2];
					// direction
					if($values[3]) {
						switch (0) {
							case strcasecmp($values[3], 'ASC'):
							$this->direction = 'ASC';
							break;
							
							case strcasecmp($values[3], 'DESC'):
							$this->direction = 'DESC';
							break;
							
							default:
							$this->direction = 'ASC';
							break;
						}
					}
					
					$queryPart = '/'.$this->direction.'/'.$this->sortColumn;
				} else {
					$queryPart = '';
				}
				
				self::$urlQueries[$prefix] = $prefix.'/'.$this->currentPage.$queryPart;
				
				$this->pagerFile = $prefix.'/%d'.$queryPart;
					
			// else check for a general match and add it
			} elseif (preg_match('/^[a-zA-Z]+\/\d+(\/(ASC|DESC)+)*(\/[a-zA-Z]+)*/i',$key)) {
				$otherPrefix = split('/',$key);
				self::$urlQueries[$otherPrefix[0]] = $key;
			} 
			
			
		}
		// Default
		if (!self::$urlQueries[$prefix]) {
			self::$urlQueries[$prefix] = $prefix.'/'.$this->currentPage;
			$this->pagerFile = $prefix.'/%d';
		}
		
		// Page size
		$this->pageSize = $pageSize;
		
		// loadData
		$result = $this->loadData($dbObject, $queryArray, $cacheHandler);
		if (PEAR::isError($result)) {
			throw new Framework_Exception('Could not load data: '.$result->getMessage());
		}
		
	}
	
	/**
	 * loadData
	 *
	 * @access 		private
	 * @param 		object 			$dbObject 		Valid DB object to query
	 * @param 		array 			$queryArray 	Query Array
	 * @param 		mixed 			$cacheHandler 	Cache handler to use with query
	 * @return 		mixed			PEAR::error on failure, void
	 */
	private function loadData($dbObject, $queryArray, $cacheHandler)
	{
		
		
		$from = ($this->pageSize *($this->currentPage)) - $this->pageSize;
		
		$queryArray['start'] = $from;
		
		if($this->sortColumn && $this->direction) {
			$queryArray['order'] = $this->sortColumn.' '.$this->direction;
		}
		
		$result = $dbObject->selectCount($queryArray, $cacheHandler);
		if (!PEAR::isError($result)) {
			$this->totalRecords = $result;
			$queryArray['count'] = $this->pageSize;
			$this->data = $dbObject->selectResult($queryArray);
			if (PEAR::isError($this->data)){
				return $this->data;
			}
			 
			$this->db->loadModule('Reverse', null, true);
			$this->tableInfo = $this->db->reverse->tableInfo($this->data, MDB2_TABLEINFO_ORDER);
			
			//$this->tableInfo = $this->data->tableInfo(DB_TABLEINFO_ORDER);
		} else {
			return $result;
		}

		
	}
	
	/**
	 * getPager
	 *
	 * @access 		public
	 * @param 		boolean 		$getObject 		Whether or not to return the Pager object or its links
	 * @param 		array 			$options 		Options for the pager
	 * @return 		mixed			Pager object, Pager links array
	 * @see 		Framework_Pager
	 */
	public function getPager($getObject = false, $options = null)
	{
		$baseOptions = array(
			'perPage' => $this->pageSize,
			'totalItems' => $this->totalRecords,
			'currentPage' => $this->currentPage,
			'fileName' => '?'.$this->pagerFile.$this->getOtherQueries($this->prefix),
			'spacesBeforeSeparator' => 0,
			'spacesAfterSeparator' => 0,
			'curPageLinkClassName' => 'curPage',
			'linkClass' => 'pagerLink',
			'firstPagePre' => '<b>',
			'firstPagePost' => '</b>',
			'lastPagePre' => '<b>',
			'lastPagePost' => '</b>',
			'separator' => ''
			
		
		);
		
		if ($options) {
			foreach ($baseOptions AS $key => $value) {
				if (!array_key_exists($key,$options)) {
					$options[$key] = $value;
				}
			}
			$pager = Framework_Pager::factory($options);
		} else {
			$pager = Framework_Pager::factory($baseOptions);
		}
		
		
		if ($getObject) {
			return $pager;
		} else {
			return $pager->getLinks();
		}
		
		
	}
	
	/**
	 * loadData
	 *
	 * @access 		private
	 * @param 		string 			$prefix 		Current prefix
	 * @return 		string			Other datagrid query strings concatenated 
	 */
	private function getOtherQueries($prefix)
	{
		if (count(self::$urlQueries) > 1) {
			$queryString = '';
			foreach (self::$urlQueries AS $key => $query) {
				if(strcasecmp($key, $prefix) != 0) {
					$queryString .= '&'.$query;
				}
			}
			
			return $queryString;
			
		} else {
			return '';
		}
	}
	
	/**
	 * loadData
	 *
	 * @access 		public
	 * @return 		array			Array of links which can be used for sorting
	 */
	public function getSortLinks()
	{
		
		$headerLinks = array();
		
		$numFields = $this->tableInfo['num_fields'];
		
		$baseURL = Framework::$request->getHTTPPath().'?';
		
		for ($i = 0; $i < $numFields; $i++) {
			if (strcasecmp($this->tableInfo[$i]['name'],$this->sortColumn) == 0) {
				if ($this->direction == 'ASC') {
					$direction = 'DESC';
				} else {
					$direction = 'ASC';
				}
			} else {
				$direction = 'ASC';
			}
			$headerLinks[$this->tableInfo[$i]['name']] = $baseURL.$this->prefix.'/'.$this->currentPage.'/'.$this->tableInfo[$i]['name'].'/'.$direction.$this->getOtherQueries($this->prefix);
		}
		
		return $headerLinks;
		
	}
	
	/**
	 * getData
	 *
	 * @access 		public
	 * @param 		integer 		$fetchmode 		Fetch Mode compatable with PEAR::db
	 * @return 		array			Array of data from the result set
	 */
	public function getData($fetchmode = MDB2_FETCHMODE_ASSOC)
	{
		if ($numRows = $this->data->numRows()) {
			while ($record = $this->data->fetchRow($fetchmode)) {
                $recordSet[] = $record;
            }
        }

        $this->data->free();
        
        return $recordSet;
		
	}
}

?>